#include"task.h"

const int processnum = 5;
std::vector<task> tasks;
//先描述，描述管道
class channel
{
public:
    //构造函数通过列表进行初始化
    channel(int fd, int childpid, const std::string &processname)
        :_fd(fd)
        ,_childpid(childpid)
        ,_processname(processname)
    {}
public:
    int _fd; //表示父进程链接某个管道的fd
    pid_t _childpid; //管道所连接表示子进程的pid
    std::string _processname; //管道所连接的子进程的名字
};

//子进程工作代码，通过父进程向管道发送的任务码找到对应的任务，然后执行
void work()
{
    while(1)
    {
        int teskcode = 0;
        int n = read(0, &teskcode, sizeof(teskcode)); //read返回的是读取到的字节的个数。 
        //然后第二个参数是要读到哪里去，第三个参数是读取的大小,这一行代码执行完后，teskcode里面保存的就是要执行的任务码
 
        if (n == sizeof(teskcode))
        {
            std::cout << "work get a command : " << getpid() << " : " << "teskcode" << teskcode << std::endl;
            if (teskcode >= 0 && teskcode < tasks.size()) tasks[teskcode]();
        }
        if (n == 0) break; //如果读到0， 说明写端关闭， 读端读到文件末尾， 就需要停止读取了
    }
}

void Initchannel(std::vector<channel>* channels)
{
    //解决方案2
    std::vector<int> oldfd;//创建一个数组记录父进程所在的文件描述符的写端
    for(int i = 0;i<processnum;i++)
    {
        //创建管道
       
        int pipefd[2];
        int n = pipe(pipefd);//创建管道文件
        if(n != 0) exit(0);//创建失败就退出程序

        //创建完管道文件然后创建管道文件对应的子进程
        pid_t id = fork();
        if(id == 0)
        {
            //将父进程所在的文件描述符写端，子进程将其关闭
            for(auto fd : oldfd) close(fd);

            //子进程模块，这里子进程去读，所以关闭写通道
            close(pipefd[1]);
            //重定向
            dup2(pipefd[0],0);

            work();//子进程进行工作的函数模块
            std::cout << "Process PID: " << getpid() << " quit" << std::endl;
            exit(0);//工作完就结束子进程
        }
        //父进程去写所以这里关闭读端
        close(pipefd[0]);

        std::string name = "process"+std::to_string(i);
        channels->push_back(channel(pipefd[1],id,name));
        //每次记录父进程写端所在的文件描述符
        oldfd.push_back(pipefd[1]); 
    }
}

void menu()
{
    std::cout << "#########################################" << std::endl;
    std::cout << "# 1、刷新野区            2、刷新技能      #" << std::endl;
    std::cout << "# 3、泉水回血            4、技能耗蓝      #" << std::endl;
    std::cout << "# 0、退出                                #" << std::endl;
    std::cout << "#########################################" << std::endl;
}

void ctrlchild(std::vector<channel>& channels)
{
    int which = 0;
    while(1)
    {
        menu();
        int selet = 0;
        std::cout<<"请输入所选择的任务";
        std::cin>>selet;
        
        //判断所选的任务码是否合法
        if(selet<=0 || selet>=5) break;
        
        //任务选择
        int taskcode = selet - 1;

        //子进程选择,轮询法
        std::cout<<"father say taskcode :" << taskcode <<" already send to " << channels[which]._childpid 
            << "process name " << channels[which]._processname << std::endl;
        //发送任务
        write(channels[which]._fd,&taskcode,sizeof(taskcode));//确定子进程，子进程所接收的任务码，和所接受的大小

        //保证所选的进程合法
        which++;
        which %= channels.size();
    }
}

void quitprocess(const std::vector<channel> &channels)
{
    //出现问题
    for(const auto &c : channels)
    {
        close(c._fd);   
        waitpid(c._childpid,nullptr,0);
    }


    // //解决方案1
    // int last = channels.size()-1;
    // for(int i = last;i>=0;i--)
    // {
    //     close(channels[i]._fd);
    //     waitpid(channels[i]._childpid,nullptr,0);
    // }



    // for(const auto &c : channels)
    // {
    //     close(c._fd);
    // }
    // for(const auto &c : channels)
    // {
    //     waitpid(c._childpid,nullptr,0);
    // }
}

int main()
{
    //加载好任务
    Loadtask(&tasks);

    //通过vector将一个个管道组织起来
    std::vector<channel> channels;

    //初始化，创建管道
    Initchannel(&channels);
    //控制子进程
    ctrlchild(channels);
    //清理收尾
    quitprocess(channels);

    return 0;
}
